package com.yyy.trans.controller;

import com.yyy.trans.aspect.ApiOperationLog;
import com.yyy.trans.entity.MyData;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StopWatch;
import org.springframework.web.bind.annotation.*;

import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.LongAdder;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.LongStream;

@RestController
@RequestMapping("/thread")
@Api("线程test1")
@Slf4j
public class ThreadTestController {

    private static final ThreadLocal<Integer> currentUser = ThreadLocal.withInitial(() -> null);

    @PostMapping("/wrong1")
    @ApiOperation("线程重用")
    public Map<String, String> wrong1(@RequestParam("userId") Integer userId) {
        String before = Thread.currentThread().getName() + ":" + currentUser.get();
        currentUser.set(userId);

        try {
            String after = Thread.currentThread().getName() + ":" + currentUser.get();
            HashMap<String, String> result = new HashMap<>();
            result.put("before", before);
            result.put("after", after);
            return result;
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            currentUser.remove();
        }
    }

    // ====================================================================================

    private static int THREAD_COUNT = 10;

    private static int ITEM_COUNT = 1000;

    // 帮助方法，用来获取一个指定元素数量模拟数据的ConcurrentHashMap
    private ConcurrentHashMap<String, Long> getData(int count) {
        return LongStream.rangeClosed(1, count)
                .boxed()
                .collect(Collectors.toConcurrentMap(i -> UUID.randomUUID().toString(), Function.identity(), (o1, o2) -> o1, ConcurrentHashMap::new));
    }

    @PostMapping("wrong2")
    @ApiOperation("concurrentHashMap使用")
    public String wrong2() throws Exception {
        // 初始为900个元素
        ConcurrentHashMap<String, Long> concurrentHashMap = getData(ITEM_COUNT - 100);
        log.info("init size:{}", concurrentHashMap.size());

        ForkJoinPool forkJoinPool = new ForkJoinPool(THREAD_COUNT);
        // 使用线程池并发处理逻辑
        forkJoinPool.execute(() -> IntStream.rangeClosed(1, 10).parallel().forEach(i -> {
            synchronized (concurrentHashMap) {
                int gap = ITEM_COUNT - concurrentHashMap.size();
                log.info("gap size:{}", gap);

                concurrentHashMap.putAll(getData(gap));
            }
        }));
        forkJoinPool.shutdown();
        forkJoinPool.awaitTermination(1, TimeUnit.HOURS);

        log.info("finish size:{}", concurrentHashMap.size());
        return concurrentHashMap.size() + "";

    }
    // ====================================================================================

    // 循环次数
    private static int LOOP_COUNT = 10000000;
    // 线程数量
    private static int THEAD_COUNT_1 = 10;
    // 元素数量
    private static int ITEM_COUNT_1 = 10;

    private Map<String, Long> normaluse() throws InterruptedException {
        ConcurrentHashMap<String, Long> freqs = new ConcurrentHashMap<>(ITEM_COUNT_1);
        ForkJoinPool forkJoinPool = new ForkJoinPool(THEAD_COUNT_1);

        forkJoinPool.execute(() -> IntStream.rangeClosed(1, LOOP_COUNT).parallel().forEach((i) -> {
            // 获得一个随机的key
            String key = "item" + ThreadLocalRandom.current().nextInt(ITEM_COUNT_1);
            synchronized (freqs) {
                if (freqs.containsKey(key)) {
//                    log.info("key出现的次数，{}", freqs.get(key) + 1);
                    freqs.put(key, freqs.get(key) + 1);
                } else {
                    freqs.put(key, 1L);
                }
            }
        }));

        forkJoinPool.shutdown();
        forkJoinPool.awaitTermination(1, TimeUnit.HOURS);

        return freqs;
    }

    @PostMapping("/wrong3")
    @ApiOperation(value = "使用Map 统计key出现的次数")
    @ApiOperationLog(description = "使用Map 统计key出现的次数")
    public Map<String, Long> wrong3() {
        Map<String, Long> normaluse = null;
        try {
            normaluse = normaluse();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return normaluse;
    }

    @PostMapping("/wrong4")
    @ApiOperation(value = "使用Map 统计key出现的次数")
    @ApiOperationLog(description = "使用Map 统计key出现的次数")
    public Map<String, Long> wrong4() {
        Map<String, Long> goodUse = null;
        try {
            goodUse = goodUse();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return goodUse;
    }

    private Map<String, Long> goodUse() throws Exception {
        ConcurrentHashMap<String, LongAdder> freqs = new ConcurrentHashMap<>(ITEM_COUNT_1);
        ForkJoinPool forkJoinPool = new ForkJoinPool(THEAD_COUNT_1);

        forkJoinPool.execute(() -> IntStream.rangeClosed(1, LOOP_COUNT).parallel().forEach((i) -> {
            String key = "item" + ThreadLocalRandom.current().nextInt(ITEM_COUNT_1);
            freqs.computeIfAbsent(key, k -> new LongAdder()).increment();
        }));

        forkJoinPool.shutdown();
        forkJoinPool.awaitTermination(1, TimeUnit.HOURS);
        return freqs.entrySet().stream().collect(
                Collectors.toMap(
                        e -> e.getKey(),
                        e -> e.getValue().longValue()
                )
        );
    }

    //---------------------------------------------------------------------------------------------------
    // 测试并发写的性能
    @PostMapping("/write1")
    @ApiOperation(value = "测试并发写的性能")
    @ApiOperationLog(description = "测试并发写的性能")
    public Map write1() {
        List<Integer> copyOnWriteArrayList = new CopyOnWriteArrayList<>();
        List<Integer> synchronizedList = Collections.synchronizedList(new ArrayList<>());
        StopWatch stopWatch = new StopWatch();
        int loopCount = 100000;
        stopWatch.start("Write:copyOnWriteArraylist");
        // 循环100000次并发往CopyOnWriteArrayList写入随机数
        IntStream.rangeClosed(1, loopCount).parallel().forEach(__ -> copyOnWriteArrayList.add(ThreadLocalRandom.current().nextInt(loopCount)));
        stopWatch.stop();
        stopWatch.start("Write:synchronizedList");
        // 循环100000次并发往加锁的Arraylist写入随机数
        IntStream.rangeClosed(1, loopCount).parallel().forEach(__ -> synchronizedList.add(ThreadLocalRandom.current().nextInt(loopCount)));
        stopWatch.stop();
        log.info(stopWatch.prettyPrint());
        Map result = new HashMap<>();
        result.put("copyOnWriteArrayList", copyOnWriteArrayList.size());
        result.put("synchronizedArrayList", synchronizedList.size());

        return result;

    }

    //测试并发读的性能
    @PostMapping("/read1")
    @ApiOperation(value = "测试并发读的性能")
    @ApiOperationLog(description = "测试并发读的性能")
    public Map read1() {
        List<Integer> copyOnWriteArrayList = new CopyOnWriteArrayList<>();
        List<Integer> synchronizedList = Collections.synchronizedList(new ArrayList<>());

        addAll(copyOnWriteArrayList);
        addAll(synchronizedList);

        StopWatch stopWatch = new StopWatch();
        int loopCount = 100000;
        int count = copyOnWriteArrayList.size();
        stopWatch.start("Read:copyOnWriteArrayList");
        // 循环100000次并发从CopyOnWriteArrayList随机读数据
        IntStream.rangeClosed(1, loopCount).parallel().forEach(__ -> copyOnWriteArrayList.get(ThreadLocalRandom.current().nextInt(count)));
        stopWatch.stop();
        // 循环100000次并发从synchronizedArrayList随机读数据
        stopWatch.start("Read:synchronizedArrayList");
        IntStream.rangeClosed(1, loopCount).parallel().forEach(__ -> synchronizedList.get(ThreadLocalRandom.current().nextInt(count)));
        stopWatch.stop();
        log.info(stopWatch.prettyPrint());
        HashMap<Object, Object> result = new HashMap<>();
        result.put("copyOnWriteArrayList", copyOnWriteArrayList.size());
        result.put("synchronizedArrayList", synchronizedList.size());
        return result;

    }

    // 填充List方法
    private void addAll(List<Integer> list) {
        list.addAll(IntStream.rangeClosed(1, 100000).boxed().collect(Collectors.toList()));
    }

    //list转换方式 坑1
    //=============================================================================================
    @PostMapping("/list1")
    @ApiOperation("List转换方式1")
    @ApiOperationLog(description = "List转换方式1")
    public void getList1() {
        int[] arr = {1, 2, 3};
        List list = Arrays.asList(arr);
        log.info("list:{} size:{} class:{}", list, list.size(), list.get(0).getClass());
        list.stream().forEach(i -> {
            System.out.println("list中的元素" + i);
        });
    }

    @PostMapping("/list2")
    @ApiOperation("List转换方式2")
    @ApiOperationLog(description = "List转换方式2")
    public void getList2() {
        int[] arr = {1, 2, 3};
        List<Integer> list = Arrays.stream(arr).boxed().collect(Collectors.toList());
        log.info("list:{} size:{} class:{}", list, list.size(), list.get(0).getClass());
        list.stream().forEach(i -> {
            System.out.println("list中的元素" + i);
        });

        Integer[] arr1 = {1, 2, 3};
        List<Integer> list1 = Arrays.asList(arr1);
        log.info("list1:{} size:{} class:{}", list1, list1.size(), list1.get(0).getClass());
        list1.stream().forEach(i -> {
            System.out.println("list1中的元素" + i);
        });

    }

    // =======================================代码加锁误区-多线程执行多个方法=====================================================
    volatile int a = 1;
    volatile int b = 1;

    public void add() {
        log.info("add start");
        for (int i = 0; i < 10000; i++) {
            a++;
            b++;
        }
        log.info("add done");
    }

    public void compare() {
        log.info("compare start");
        for (int i = 0; i < 10000; i++) {
            if (a < b) {
                log.info("a:{}, b:{},{}", a, b, a > b);
            }
        }
        log.info("compare done");
    }

    @PostMapping("/compare1")
    @ApiOperation("线程安全问题1")
    @ApiOperationLog(description = "线程安全问题1")
    public void threadCompare() {
        ThreadTestController threadTestController = new ThreadTestController();
        new Thread(threadTestController::add).start();
        new Thread(threadTestController::compare).start();
    }


//================================================代码加锁误区-==============================================

    AtomicInteger a1 = new AtomicInteger(1);
    AtomicInteger b1 = new AtomicInteger(1);

    public void add1() {
        log.info("add start");
        for (int i = 0; i < 10000; i++) {
            a1.getAndIncrement();
            b1.getAndIncrement();
        }
        log.info("add done");
    }

    public void compare1() {
        log.info("compare start");
        for (int i = 0; i < 10000; i++) {
            if (a1.get() < b1.get()) {
                log.info("a1:{}, b1:{},{}", a1, b1, a1.get() > b1.get());
            }
        }
        log.info("compare done");
    }

    @PostMapping("/compare2")
    @ApiOperation("线程安全问题解决思路1")
    @ApiOperationLog(description = "线程安全问题解决思路1")
    public void threadCompare1() {
        ThreadTestController threadTestController = new ThreadTestController();
        new Thread(threadTestController::add1).start();
        new Thread(threadTestController::compare1).start();
    }

    @GetMapping("/getCount")
    @ApiOperation("静态变量累加线程安全问题")
    @ApiOperationLog(description = "静态变量累加线程安全问题")
    public int wrong(@RequestParam(value = "count", defaultValue = "1000000") int count) {
        MyData.reset();
        //多线程循环一定次数调用Data类不同实例的wrong方法
        IntStream.rangeClosed(1, count).parallel().forEach(i -> new MyData().wrong());
        return MyData.getCounter();
    }


    private List<Integer> data = new ArrayList<>();

    private void slow() {
        try {
            TimeUnit.MICROSECONDS.sleep(10);
        } catch (InterruptedException e) {
        }
    }

    @GetMapping("/wrongLock")
    @ApiOperation("错误的加锁粒度")
    @ApiOperationLog(description = "错误的加锁粒度")
    public int wrong1() {
        long begin = System.currentTimeMillis();
        IntStream.rangeClosed(1, 1000).parallel().forEach(i -> {
            //加锁粒度太粗
            synchronized (this) {
                slow();
                data.add(i);
            }
        });
        log.info("took:{}", System.currentTimeMillis() - begin);
        return data.size();
    }

    @GetMapping("/rightLock")
    @ApiOperation("正确的加锁粒度")
    @ApiOperationLog(description = "正确的加锁粒度")
    public int right() {
        long begin = System.currentTimeMillis();
        IntStream.rangeClosed(1, 1000).parallel().forEach(i -> {
            slow();
            //只对需要加锁的数据加锁
            synchronized (data) {
                data.add(i);
            }
        });
        log.info("took:{}", System.currentTimeMillis() - begin);
        return data.size();
    }

    //=================================线程死锁======================================

    private ConcurrentHashMap<String, Item> items = new ConcurrentHashMap<>();

    public ThreadTestController() {
        IntStream.range(0, 10).forEach(i -> items.put("item" + i, new Item("item" + i)));
    }
    @Data
    @RequiredArgsConstructor
    public static class Item {
        final String name;//商品名

        int remaining = 1000;// 商品库存

        @ToString.Exclude//ToString 不包含这个字段
        ReentrantLock lock = new ReentrantLock();//每个商品都有对应的锁
    }

    //模拟选购商品，每次从商品清单items中随机选三个item商品
    private List<Item> createCart() {
        return IntStream.rangeClosed(1, 3)
                .mapToObj(i -> "item" + ThreadLocalRandom.current().nextInt(items.size()))
                .map(name -> items.get(name)).collect(Collectors.toList());

    }

    //生成订单 遍历购物车中的商品，获取该商品的锁 尝试扣减库存
    private boolean createOrder(List<Item> order) {
        List<ReentrantLock> locks = new ArrayList<>();

        for (Item item : order) {
            try {
                if (item.lock.tryLock(10, TimeUnit.SECONDS)) {
                    locks.add(item.lock);
                } else {
                    locks.forEach(ReentrantLock::unlock);
                    return false;
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        try {
            order.forEach(item -> item.remaining--);
        } finally {
            locks.forEach(ReentrantLock::unlock);
        }
        return true;
    }

    @PostMapping("/deadLock")
    @ApiOperation("线程死锁问题")
    @ApiOperationLog(description = "线程死锁问题")
    public long DeadLock() {
        long begin = System.currentTimeMillis();
        // 并发进行100次下单操作，统计成功次数
        long success = IntStream.rangeClosed(1, 100).parallel()
                .mapToObj(i -> {
                    List<Item> cart = createCart();
                    return createOrder(cart);
                })
                .filter(result -> result)
                .count();
        log.info("success:{} totalRemaining:{} took:{}ms items:{}",
                success,
                items.entrySet().stream().map(item -> item.getValue().remaining).reduce(0, Integer::sum),
                System.currentTimeMillis() - begin, items);
        return success;
    }

    @PostMapping("/deadLockRight")
    @ApiOperation("解决线程死锁问题")
    @ApiOperationLog(description = "解决线程死锁问题")
    public long DeadLockRight() {
        long begin = System.currentTimeMillis();
        // 并发进行100次下单操作，统计成功次数
        long success = IntStream.rangeClosed(1, 100).parallel()
                .mapToObj(i -> {
                    List<Item> cart = createCart().stream()
                            .sorted(Comparator.comparing(Item::getName))
                            .collect(Collectors.toList());
                    return createOrder(cart);
                })
                .filter(result -> result)
                .count();
        log.info("success:{} totalRemaining:{} took:{}ms items:{}",
                success,
                items.entrySet().stream().map(item -> item.getValue().remaining).reduce(0, Integer::sum),
                System.currentTimeMillis() - begin, items);
        return success;
    }

}